/*
 * eren_opcode_function.h
 *
 *  Created on: 2012-7-12
 *      Author: Steve Liu
 *       Email: eshifang@gmail.com
 *   CopyRight: http://www.eshifang.com
 *
 */

#ifndef EREN_OPCODE_FUNCTION_H_
#define EREN_OPCODE_FUNCTION_H_

#define BUFF_SIZE 2048

static void eren_convert_value_of_zval_to_string(zval val, char *buffer)
{
	memset(buffer, 0, BUFF_SIZE);
	switch (val.type) {
		case IS_NULL:
			sprintf(buffer, "NULL");
			break;
		case IS_LONG:
			sprintf(buffer, "Int:%ld", val.value.lval);
			break;
		case IS_BOOL:
			if(val.value.lval == 0) sprintf(buffer, "Bool:FALSE");
			else sprintf(buffer, "Bool:TRUE");
			break;
		case IS_RESOURCE:
			sprintf(buffer, "Resource:%ld", val.value.lval);
			break;
		case IS_DOUBLE:
			sprintf(buffer, "Double:%lf", val.value.dval);
			break;
		case IS_STRING:
			sprintf(buffer, "String:%s", val.value.str.val);
			break;
		case IS_ARRAY:
			sprintf(buffer, "Array");
			break;
		case IS_OBJECT:
			sprintf(buffer, "Object");
			break;
		case IS_CONSTANT:
			sprintf(buffer, "Constant");
			break;
		case IS_CONSTANT_ARRAY:
			sprintf(buffer, "ConstantArray");
			break;
		default:
			sprintf(buffer, "UNKONW");
			break;
	}
}

static void eren_convert_type_of_znode_to_string(znode node, char *buffer)
{
	memset(buffer, 0, BUFF_SIZE);
    switch(node.op_type)
    {
    	case IS_CONST:
    		sprintf(buffer, "CONST");
    		break;
    	case IS_TMP_VAR:
    		sprintf(buffer, "TMP_VAR");
    		break;
    	case IS_VAR:
    		sprintf(buffer, "VAR");
    		break;
    	case IS_UNUSED:
    		sprintf(buffer, "UNUSED");
    		break;
    	case IS_CV:
    		sprintf(buffer, "CV");
    		break;
    	default:
    		sprintf(buffer, "UNKNOW");
    }
}

static char *eren_convert_value_of_znode_to_string(znode node, char *buffer)
{
	memset(buffer, 0, BUFF_SIZE);
    switch(node.op_type)
    {
    	case IS_CONST:
    		eren_convert_value_of_zval_to_string(node.u.constant, buffer);
    		break;
    	case IS_TMP_VAR:
    		sprintf(buffer, "#%u", node.u.var);
    		break;
    	case IS_VAR:
    		sprintf(buffer, "$%u", node.u.var);
    		break;
    	case IS_UNUSED:
    		sprintf(buffer, "UNUSED");
    		break;
    	case IS_CV:
    		sprintf(buffer, "~%u", node.u.var);
    		break;
    	default:
    		sprintf(buffer, "UNKNOW");
    }
}

static zval *eren_opcode_to_zval(zend_op op_code)
{
    zval *ret = NULL;
    char *buffer = (char *)emalloc(BUFF_SIZE);

    MAKE_STD_ZVAL(ret);
    array_init(ret);

    //assign the LineNo to array;
    add_assoc_long(ret, "lineno", op_code.lineno);
    //assign the opcode string to array;
    add_assoc_string(ret, "opcode", eren_opcode_mapping[op_code.opcode], 1);
    //assign the type of op1 to array;
    eren_convert_type_of_znode_to_string(op_code.op1, buffer);
    add_assoc_string(ret, "op1_type", buffer, 1);
    //assign the type of op2 to array;
    eren_convert_type_of_znode_to_string(op_code.op2, buffer);
    add_assoc_string(ret, "op2_type", buffer, 1);
    //assign the type of result to array;
    eren_convert_type_of_znode_to_string(op_code.result, buffer);
    add_assoc_string(ret, "result_type", buffer, 1);
    //assign the value of op1 to array;
    eren_convert_value_of_znode_to_string(op_code.op1, buffer);
    add_assoc_string(ret, "op1_value", buffer, 1);
    //assign the value of op2 to array;
    eren_convert_value_of_znode_to_string(op_code.op2, buffer);
    add_assoc_string(ret, "op2_value", buffer, 1);
    //assign the value of result to array;
    eren_convert_value_of_znode_to_string(op_code.result, buffer);
    add_assoc_string(ret, "result_value", buffer, 1);
    //assign the extended_value to array;
    add_assoc_long(ret, "extended_value", op_code.extended_value);

    efree(buffer);
    buffer = NULL;

    return ret;
}

static zval *eren_op_array_to_zval_array(zend_op_array *op_array)
{
	int i = 0;
	zval *ret_array = NULL;
	zval *temp = NULL;

	MAKE_STD_ZVAL(ret_array);
	array_init(ret_array);

	if(op_array)
	{
		for(i=0; i<op_array->last; i++)
		{
			temp = eren_opcode_to_zval(op_array->opcodes[i]);
			add_next_index_zval(ret_array, temp);
		}
	}

	return ret_array;
}

static void eren_print_op_code(zend_op op_code)
{
	char *buffer = (char *)emalloc(BUFF_SIZE);

	printf("lineno:%ld\r\n", op_code.lineno);
	printf("opcode:%s\r\n", eren_opcode_mapping[op_code.opcode]);
	eren_convert_type_of_znode_to_string(op_code.op1, buffer);
	printf("op1_type:%s\r\n", buffer);
	eren_convert_type_of_znode_to_string(op_code.op2, buffer);
	printf("op2_type:%s\r\n", buffer);
	eren_convert_type_of_znode_to_string(op_code.result, buffer);
	printf("result_type:%s\r\n", buffer);
	eren_convert_value_of_znode_to_string(op_code.op1, buffer);
	printf("op1_value:%s\r\n", buffer);
	eren_convert_value_of_znode_to_string(op_code.op2, buffer);
	printf("op2_value:%s\r\n", buffer);
	eren_convert_value_of_znode_to_string(op_code.result, buffer);
	printf("result_value:%s\r\n", buffer);
	printf("extended_value:%ld\r\n", op_code.extended_value);

	efree(buffer);
	buffer = NULL;
}

static void eren_print_op_array(zend_op_array *op_array)
{
	if(!op_array) return;

	int i = 0;
	for(i=0; i<op_array->last; i++)
	{
		eren_print_op_code(op_array->opcodes[i]);
		printf("======================================\r\n");
	}
}

#endif /* EREN_OPCODE_FUNCTION_H_ */
